﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Web;
using Com.Toook.AutoDownloadHtml.DownloadHtml.ParseObj;
using Com.Toook.AutoDownloadHtml.Util;
using NSoup.Select;
using NSoup.Nodes;

namespace Com.Toook.AutoDownloadHtml.DownloadHtml
{
    /// <summary>
    /// 解析分类页面
    /// </summary>
    public class ParseKindPage
    {
        private NSoup.Nodes.Document doc = null;
        private string path = HttpContext.Current.Request.PhysicalApplicationPath
                + "\\App_Data\\NewsParseRule_Sohu.xml";
        private string kindName;//网站中某一个分类的Id名字

        public ParseKindPage(string xmlFilePath, string kindId)
        {
            this.path = xmlFilePath;
            this.kindName = kindId;
        }
        #region 根据Step对象获取分类链接地址
        private void GetByKindList(List<List<Rule>> kindList)
        {
            foreach(List<Rule> rules in kindList)
            {
                foreach(Rule rule in rules)
                {
                    while(rule.NextStep())
                    {
                        Elements initElements = GetInitElements();
                        ParseStepByStep(initElements, rule);
                    }                    
                }
            }
        }
        /// <summary>
        /// 根据分类中的规则列表获取元素
        /// </summary>
        /// <param name="ruleList"></param>
        private void GetByKindList(List<Rule> ruleList)
        {           
            foreach(Rule rule in ruleList)
            {
                while(rule.NextStep())
                {
                    Elements initElements = GetInitElements();
                    ParseStepByStep(initElements, rule);
                }
            }           
        }
        private Elements GetInitElements()
        {
            Elements eles = new Elements();
            doc = NSoup.NSoupClient.Connect("http://www.360bbk.com/").Get();
            eles = doc.GetElementsByClass("nav");
            return eles;
        }
        private void ParseStepByStep(Elements initElements, Rule rule)
        {
            StringBuilder sb = new StringBuilder();

            //i < initElements.Count 主要是判断 GetBy 返回的initElements新结果集是否有数据，没有就跳出循环
            for(int i = 0; rule.NextStep() && i < initElements.Count; i++)
            {
                //Elements eles = GetBy(initElements, stepList.currentStep);
                //initElements = eles;

                initElements = GetAndMergeElements(initElements, rule.CurrentStep);
                //GetBy会遍历入参initElements，合并每个节点的结果后返回，将新结果赋给initElements，然后进行下一步操作。
                //stepList.Next()判断是否还有下一步。 
            }

            //Leaf 获取最终的链接地址
            Elements results = new Elements();
            /*foreach(Element e in initElements)
            {
                results = GetElements(rule.LeafStep, e); //e.GetElementsByTag("a");

                foreach(Element item in results)
                {
                    sb.Append("title=" + item.Text() + ", link=" + item.AbsUrl("href") + "<br/>");
                }
            }*/
            for (int i = 0; rule.NextLeaf() && i < initElements.Count; i++)
            {
                initElements = GetAndMergeElements(initElements, rule.CurrentLeaf);                
            }

            //showText.Text = sb.ToString();
        }
        /// <summary>
        /// 遍历elements节点，获取每个节点中符合step条件的子节点集合；合并所有子节点集合，作为最终的Elements结果集
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        private Elements GetAndMergeElements(Elements elements, Step step)
        {
            Elements results = new Elements();            
            foreach(Element e in elements)
            {
                Elements eles = GetElements(step, e);
                foreach(Element r in eles)
                {
                    if(r.ChildNodes.Count > 0)
                    {
                        results.Add(r);
                    }
                }
            }
            return results;
        }
        /// <summary>
        /// 根据Step获取Elements元素
        /// </summary>
        /// <param name="step"></param>
        /// <param name="element"></param>
        /// <returns></returns>
        private Elements GetElements(Step step, Element element)
        {
            Elements results = new Elements();
            int index;//Elements(index);
            switch(step.GetBy)
            {
                case ByType.Id:
                    Element ele = element.GetElementById(step.AttrName);
                    if(ele != null && ele.ChildNodes.Count > 0)
                    {
                        results.Add(ele);
                    }
                    break;
                case ByType.Tag:
                    results = element.GetElementsByTag(step.AttrName);
                    break;
                case ByType.Class:
                    results = element.GetElementsByClass(step.AttrName);
                    break;
                case ByType.ContainText:
                    results = element.GetElementsContainingText(step.AttrName);
                    break;
                case ByType.ContainOwnText:
                    results = element.GetElementsContainingOwnText(step.AttrName);
                    break;
                case ByType.MatchText:
                    results = element.GetElementsMatchingText(step.AttrName);
                    break;
                case ByType.MatchOwnText:
                    results = element.GetElementsMatchingOwnText(step.AttrName);
                    break;
                case ByType.Attr:
                    results = element.GetElementsByAttribute(step.AttrName);
                    break;
                case ByType.AttrStart:
                    results = element.GetElementsByAttributeStarting(step.AttrName);
                    break;
                case ByType.IndexEquals:                    
                    int.TryParse(step.AttrName, out index);
                    results = element.GetElementsByIndexEquals(index);
                    break;
                case ByType.IndexLessThan:
                    int.TryParse(step.AttrName, out index);
                    results = element.GetElementsByIndexLessThan(index);
                    break;
                case ByType.IndexGreaterThan:
                    int.TryParse(step.AttrName, out index);
                    results = element.GetElementsByIndexGreaterThan(index);
                    break;
                //属性名，属性值
                case ByType.AttrValue:
                    results = element.GetElementsByAttributeValue(step.AttrName, step.AttrValue);
                    break;
                case ByType.AttrValueStart:
                    results = element.GetElementsByAttributeValueStarting(step.AttrName, step.AttrValue);
                    break;
                case ByType.AttrValueEnd:
                    results = element.GetElementsByAttributeValueEnding(step.AttrName, step.AttrValue);
                    break;
                case ByType.AttrValueContain:
                    results = element.GetElementsByAttributeValueContaining(step.AttrName, step.AttrValue);
                    break;
                case ByType.AttrValueMatch:
                    results = element.GetElementsByAttributeValueMatching(step.AttrName, step.AttrValue);
                    break;
                default:
                    LogHelper.Log.Error("GetElements(Step step, Element element) GetBy=" + step.GetBy + "不合法, 文件：" + path);
                    break;
            }

            return results;
        }
        #endregion
        //GetStepListFromXml();
        #region 根据XML解析出Step对象
        /*
        private void GetStepListFromXml()
        {
            XmlDocument docXml = new XmlDocument();
            docXml.Load(path);
            XmlElement root = docXml.DocumentElement;

            XmlNodeList kinds = root.SelectNodes("kind");
            //根据网站分类数初始化kindList
            List<List<Rule>> kindList = new List<List<Rule>>(kinds.Count);

            //获取各个分类
            foreach(XmlNode kind in kinds)
            {
                //一个分类可能有多个规则rule
                XmlNodeList rules = kind.SelectNodes("rule");
                List<Rule> ruleList = ParseRuleList(rules);
                kindList.Add(ruleList);
            }

            ShowInfo(kindList);
        }
        private void ShowInfo(List<List<Rule>> kindList)
        {
            StringBuilder sb = new StringBuilder();
            foreach(List<Rule> rules in kindList)
            {
                sb.Append("Rule: <br/>");
                foreach(Rule stepList in rules)
                {
                    sb.Append("--StepList:<br/>");
                    while(stepList.Next())
                    {
                        sb.Append("----Step: ");
                        sb.Append(getObjStr(stepList.CurrentStep)).Append("<br/>");
                    }
                    sb.Append("----leaf: ");
                    sb.Append(getObjStr(stepList.LeafStep)).Append("<br/>").ToString();
                }
            }
            //showText.Text = sb.ToString();
        }
        private string getObjStr(Step step)
        {
            string result = "";
            switch(step.GetBy)
            {
                case ByType.Attr:
                    result = "GetBy = " + step.GetBy.ToString() + ", Name = " + step.AttrName;
                    break;
                case ByType.AttrValue:
                    result = "GetBy = " + step.GetBy.ToString() + ", Name = " + step.AttrName + ", Val = " + step.AttrValue;
                    break;
                case ByType.AttrValueStart:
                    result = "GetBy = " + step.GetBy.ToString() + ", Name = " + step.AttrName + ", Val = " + step.AttrValue;
                    break;
                default:
                    break;
            }

            return result;
        }
       */
        
        /// <summary>
        /// 将分类中的每个规则rule解析为对应的StepList
        /// </summary>
        private List<Rule> ParseRuleList(XmlNodeList rules)
        {
            List<Rule> ruleList = new List<Rule>(rules.Count);//一个分类可能有多个解析规则

            foreach(XmlNode rule in rules)
            {
                XmlNodeList steps = rule.ChildNodes; //获取step和leaf 节点
                Rule stepList = ParseRule(steps);
                ruleList.Add(stepList);
            }

            return ruleList;
        }
        /// <summary>
        /// 解析出每个Step和Leaf,添加到Rule
        /// </summary>
        /// <param name="steps"></param>
        /// <returns></returns>
        private Rule ParseRule(XmlNodeList steps)
        {
            Rule rule = new Rule();//一个规则rule对应一个StepList        
            foreach(XmlNode s in steps)
            {
                Step step = ParseStep(s);//将xml中的每个step节点解析为Step对象
                if(s.Name == "step")
                {
                    rule.AddStep(step);
                }
                else if(s.Name == "leaf")
                {
                    rule.AddLeaf(step);
                }
                else
                {
                    LogHelper.Log.Error("ParseRule() s.Name 不是step和leaf，不合法。文件：" + this.path);
                }
            }

            return rule;
        }
        /// <summary>
        /// 将xml中的每个step节点解析为Step对象
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private Step ParseStep(XmlNode node)
        {
            Step step = new Step();
            step.AttrName = node.Attributes["name"].Value;
            step.AttrValue = node.Attributes["value"] == null ? "" : node.Attributes["value"].Value;
            string getBy = node.Attributes["getBy"].Value;
            switch(getBy)
            {
                case "Id":
                    step.GetBy = ByType.Id;                    
                    break;
                case "Class":
                    step.GetBy = ByType.Class;                    
                    break;
                case "Tag":
                    step.GetBy = ByType.Tag;
                    break;
                case "Attr":
                    step.GetBy = ByType.Attr;
                    break;
                case "AttrStart":
                    step.GetBy = ByType.AttrStart;
                    break;
                case "ContainText":
                    step.GetBy = ByType.ContainText;
                    break;
                case "ContainOwnText":
                    step.GetBy = ByType.ContainOwnText;
                    break;
                case "MatchText":
                    step.GetBy = ByType.MatchText;
                    break;
                case "MatchOwnText":
                    step.GetBy = ByType.MatchOwnText;
                    break;
                case "IndexEquals":
                    step.GetBy = ByType.IndexEquals;
                    break;
                case "IndexLessThan":
                    step.GetBy = ByType.IndexLessThan;
                    break;
                case "IndexGreaterThan":
                    step.GetBy = ByType.IndexGreaterThan;
                    break;
                //属性名，属性值
                case "AttrValue":
                    step.GetBy = ByType.AttrValue;
                    break;
                case "AttrValueStart":
                    step.GetBy = ByType.AttrValueStart;
                    break;
                case "AttrValueEnd":
                    step.GetBy = ByType.AttrValueEnd;
                    break;
                case "AttrValueContain":
                    step.GetBy = ByType.AttrValueContain;
                    break;
                case "AttrValueMatch":
                    step.GetBy = ByType.AttrValueMatch;
                    break;
             
                default:
                    LogHelper.Log.Error("ParseStep() getBy=" + getBy + "不合法, 文件：" + path);
                    break;
            }
            return step;
        }
        #endregion
    }
}
